home *** CD-ROM | disk | FTP | other *** search
/ Windows 95 API Bible / Windows 95 API Bible 3 Disc Set.iso / Win32 API Bible Book 3 of 3.iso / chapte16 / midiout.c < prev    next >
C/C++ Source or Header  |  1996-04-29  |  21KB  |  604 lines

  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include "midiout.h"
  4.  
  5. #if defined (WIN32)
  6.     #define IS_WIN32 TRUE
  7. #else
  8.     #define IS_WIN32 FALSE
  9. #endif
  10.  
  11. #define IS_NT      IS_WIN32 && (BOOL)(GetVersion() < 0x80000000)
  12. #define IS_WIN32S  IS_WIN32 && (BOOL)(!(IS_NT) && (LOBYTE(LOWORD(GetVersion()))<4))
  13. #define IS_WIN95   (BOOL)(!(IS_NT) && !(IS_WIN32S)) && IS_WIN32
  14.  
  15. HINSTANCE hInst;   // current instance
  16.  
  17. LPCTSTR lpszAppName = "MyApp";
  18. LPCTSTR lpszTitle   = "MIDI Output"; 
  19.  
  20. BOOL RegisterWin95( CONST WNDCLASS* lpwc );
  21.  
  22.  
  23. int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
  24.                       LPTSTR lpCmdLine, int nCmdShow)
  25. {
  26.    MSG      msg;
  27.    HWND     hWnd; 
  28.    WNDCLASS wc;
  29.  
  30.    wc.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  31.    wc.lpfnWndProc   = (WNDPROC)WndProc;       
  32.    wc.cbClsExtra    = 0;                      
  33.    wc.cbWndExtra    = 0;                      
  34.    wc.hInstance     = hInstance;              
  35.    wc.hIcon         = LoadIcon (hInstance, lpszAppName); 
  36.    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  37.    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  38.    wc.lpszMenuName  = lpszAppName;              
  39.    wc.lpszClassName = lpszAppName;              
  40.  
  41.    if ( IS_WIN95 )
  42.    {
  43.       if ( !RegisterWin95( &wc ) )
  44.          return( FALSE );
  45.    }
  46.    else if ( !RegisterClass( &wc ) )
  47.       return( FALSE );
  48.  
  49.    hInst = hInstance; 
  50.  
  51.    hWnd = CreateWindow( lpszAppName, 
  52.                         lpszTitle,    
  53.                         WS_OVERLAPPEDWINDOW, 
  54.                         CW_USEDEFAULT, 0, 
  55.                         CW_USEDEFAULT, 0,  
  56.                         NULL,              
  57.                         NULL,              
  58.                         hInstance,         
  59.                         NULL               
  60.                       );
  61.  
  62.    if ( !hWnd ) 
  63.       return( FALSE );
  64.  
  65.    ShowWindow( hWnd, nCmdShow ); 
  66.    UpdateWindow( hWnd );         
  67.  
  68.    while( GetMessage( &msg, NULL, 0, 0) )   
  69.    {
  70.       TranslateMessage( &msg ); 
  71.       DispatchMessage( &msg );  
  72.    }
  73.  
  74.    return( msg.wParam ); 
  75. }
  76.  
  77.  
  78. BOOL RegisterWin95( CONST WNDCLASS* lpwc )
  79. {
  80.     WNDCLASSEX wcex;
  81.  
  82.    wcex.style         = lpwc->style;
  83.    wcex.lpfnWndProc   = lpwc->lpfnWndProc;
  84.    wcex.cbClsExtra    = lpwc->cbClsExtra;
  85.    wcex.cbWndExtra    = lpwc->cbWndExtra;
  86.    wcex.hInstance     = lpwc->hInstance;
  87.    wcex.hIcon         = lpwc->hIcon;
  88.    wcex.hCursor       = lpwc->hCursor;
  89.    wcex.hbrBackground = lpwc->hbrBackground;
  90.    wcex.lpszMenuName  = lpwc->lpszMenuName;
  91.    wcex.lpszClassName = lpwc->lpszClassName;
  92.  
  93.    // Added elements for Windows 95.
  94.    //...............................
  95.    wcex.cbSize = sizeof(WNDCLASSEX);
  96.    wcex.hIconSm = LoadImage(wcex.hInstance, lpwc->lpszClassName, 
  97.                             IMAGE_ICON, 16, 16,
  98.                             LR_DEFAULTCOLOR );
  99.             
  100.    return RegisterClassEx( &wcex );
  101. }
  102.  
  103. #define USR_SHORTMSG  (WM_USER+1)
  104.  
  105. #define MSG_LEN          1024
  106. #define DATABLOCK_SIZE   1024L
  107.  
  108.  
  109. enum Status
  110. {
  111.   StatusOn,
  112.   StatusError,
  113.   StatusOff,
  114.   StatusSendSysExcl,
  115. } eStatus = StatusOff;
  116.  
  117. char     msg[MSG_LEN+1];
  118.  
  119. HWND     hListBox = NULL;
  120. MMRESULT rc;
  121.  
  122. UINT       nDevId = 0;
  123. HMIDIOUT   hmo = NULL;
  124. MIDIHDR*   pmh = NULL;
  125.  
  126. typedef struct
  127. {
  128.    DWORD ms;        // time in milliseconds
  129.    DWORD dwMidiMsg; // short MIDI message
  130. } MIDIMSG, *LPMIDIMSG;
  131.  
  132. typedef struct
  133. {
  134.    DWORD    dwNextMsg;  // next MIDI short message to play
  135.    DWORD    ms;         // current position in milliseconds
  136. } MIDISONG, *LPMIDISONG;
  137.  
  138. #define SONG_LEN  6 
  139.  
  140. MIDIMSG MidiMsg[SONG_LEN] = {   0, 0x403c90,   // C on  (C3)
  141.                               200, 0x003c90,   // C off
  142.                               400, 0x404c90,   // G on
  143.                               600, 0x004c90,   // G off
  144.                               800, 0x404090,   // E on
  145.                              1000, 0x004090    // E off
  146.                             };
  147. MIDISONG   MidiSong;
  148. TIMECAPS   tc;
  149. UINT       nTimerId;
  150. UINT       nTimerRes;
  151.  
  152. #define EXCL_LEN  6
  153.  
  154. BYTE ExclData[EXCL_LEN] = { 0xf0,  // beginf system exclusive
  155.                             0x41,  // data
  156.                             0x41, 
  157.                             0x00, 
  158.                             0x23, 
  159.                             0xf7   // EOX (End of Exclusive)
  160.                           };
  161.  
  162. VOID CALLBACK timeProc(UINT nTimerId, UINT msg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2);
  163.  
  164.  
  165. LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  166. {
  167.    switch( uMsg )
  168.    {
  169.       case WM_CREATE :
  170.               
  171.               eStatus = StatusOff;
  172.               SetWindowText(hWnd, "MIDI Output - Off");
  173.  
  174.               // allocate MIDIHDR buffer block
  175.                //..............................
  176.  
  177.               pmh = HeapAlloc( GetProcessHeap(),                         
  178.                                 HEAP_ZERO_MEMORY,                         
  179.                                 sizeof(MIDIHDR) );                        
  180.               if (pmh)                                                  
  181.               {                                                            
  182.                   pmh->lpData = HeapAlloc( GetProcessHeap(),             
  183.                                            HEAP_ZERO_MEMORY,             
  184.                                            DATABLOCK_SIZE);                       
  185.                   pmh->dwBufferLength = DATABLOCK_SIZE;
  186.               }
  187.               else
  188.                   return( -1 );
  189.  
  190.               // create ListBox
  191.               //...............
  192.  
  193.               hListBox = CreateWindow( "LISTBOX", "",    
  194.                                        WS_CHILD | LBS_NOTIFY | 
  195.                                        WS_VSCROLL | WS_BORDER | 
  196.                                        WS_VISIBLE | LBS_NOINTEGRALHEIGHT, 
  197.                                        0, 0, 
  198.                                        0, 0,  
  199.                                        hWnd,              
  200.                                        (HMENU)101,              
  201.                                        hInst,         
  202.                                        NULL );
  203.               break;
  204.  
  205.       case WM_SIZE :
  206.               MoveWindow( hListBox, 0, 0, 
  207.                           LOWORD( lParam ), 
  208.                           HIWORD( lParam ), TRUE );
  209.               break;
  210.  
  211.       case WM_COMMAND :
  212.               switch( LOWORD( wParam ) )
  213.               {
  214.                  case IDM_TEST:
  215.                         {
  216.                            MIDIOUTCAPS moc;
  217.                            KEYARRAY    DrumPatchCache;
  218.                            KEYARRAY    InstPatchCache;
  219.                            
  220.                            if (eStatus == StatusOn || eStatus == StatusSendSysExcl)
  221.                                break; // already busy
  222.  
  223.                            SendMessage(hListBox, LB_RESETCONTENT, 0, 0);
  224.  
  225.                            // open MIDI output device
  226.                            //........................
  227.  
  228.                            rc = midiOutOpen(&hmo, nDevId, (DWORD)NULL, 
  229.                                            (DWORD)NULL, CALLBACK_NULL);
  230.                            
  231.                            if (rc != MMSYSERR_NOERROR)
  232.                            {
  233.                                midiOutGetErrorText(rc, msg, MSG_LEN);
  234.                                MessageBox(hWnd, msg, NULL, MB_OK);
  235.                                break;
  236.                            }
  237.  
  238.                            rc = midiOutGetDevCaps(nDevId, &moc, sizeof(MIDIOUTCAPS));
  239.                            
  240.                            if (moc.dwSupport & MIDICAPS_CACHE)
  241.                            {
  242.                                midiOutCacheDrumPatches(hmo, nDevId, DrumPatchCache, MIDI_CACHE_QUERY);
  243.                                midiOutCachePatches    (hmo, nDevId, InstPatchCache, MIDI_CACHE_QUERY);
  244.                            }
  245.  
  246.                            if (moc.dwSupport & MIDICAPS_VOLUME)
  247.                            {
  248.                                DWORD dwVol;
  249.  
  250.                                // set volume level to at least 80%
  251.                                //.................................
  252.  
  253.                                rc = midiOutGetVolume(hmo, &dwVol);
  254.  
  255.                                if (rc == MMSYSERR_NOERROR)
  256.                                    if (LOWORD(dwVol) < 0xCCCC ||
  257.                                         (moc.dwSupport & MIDICAPS_LRVOLUME && 
  258.                                           HIWORD(dwVol) < 0xCCCC ) )
  259.                                        rc = midiOutSetVolume(hmo, (DWORD)MAKELONG(0xCCCC, 0xCCCC) );
  260.                            }
  261.  
  262.                            // display device capablilites
  263.                            //............................
  264.  
  265.                            SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)moc.szPname);
  266.  
  267.                            sprintf(msg, "Voices:%d  Notes:%d  Channel Mask:%x",
  268.                                    moc.wVoices, moc.wNotes, moc.wChannelMask);
  269.                            SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)msg);
  270.  
  271.                            SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)"");
  272.                            SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)"Supports the following:");
  273.  
  274.                            // wTechnology
  275.  
  276.                            if (moc.wTechnology & MOD_MIDIPORT)
  277.                                SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)"   MIDI hardware port");
  278.  
  279.                            if (moc.wTechnology & MOD_SQSYNTH)
  280.                                SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)"   Square wave synthesizer");
  281.  
  282.                            if (moc.wTechnology & MOD_FMSYNTH)
  283.                                SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)"   FM synthesizer");
  284.  
  285.                            if (moc.wTechnology & MOD_MAPPER)
  286.                                SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)"   Microsoft MIDI Mapper");
  287.  
  288.                            // dwSupport
  289.                            
  290.                            if (moc.dwSupport & MIDICAPS_VOLUME)
  291.                                SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)"   Volume control");
  292.  
  293.                            if (moc.dwSupport & MIDICAPS_LRVOLUME)
  294.                                SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)"   Separate left and right volume control");
  295.  
  296.                            if (moc.dwSupport & MIDICAPS_CACHE)
  297.                                SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)"   Patch caching");
  298.  
  299.                            // close MIDI output device
  300.                            //.........................
  301.  
  302.                            if (hmo)
  303.                            {
  304.                                midiOutReset(hmo);
  305.                                midiOutClose(hmo);
  306.                            }
  307.  
  308.                         }
  309.                         break;
  310.  
  311.                  case IDM_ON :
  312.                         {
  313.                            if (eStatus == StatusOn || eStatus == StatusSendSysExcl)
  314.                                break; // already busy
  315.  
  316.                            // reset MIDI song struct
  317.                            //.......................
  318.  
  319.                            MidiSong.dwNextMsg = 0;
  320.                            MidiSong.ms        = 0;
  321.  
  322.                            SendMessage(hListBox, LB_RESETCONTENT, 0, 0);
  323.  
  324.                            // setup high resolution MIDI timer
  325.                            //.................................
  326.  
  327.                            rc = timeGetDevCaps(&tc, sizeof(TIMECAPS));
  328.  
  329.                            if (rc == TIMERR_NOERROR)
  330.                            {
  331.                                nTimerRes = min( max(tc.wPeriodMin, 1), tc.wPeriodMax);
  332.  
  333.                                timeBeginPeriod(nTimerRes);
  334.                            
  335.                                nTimerId = timeSetEvent(nTimerRes, nTimerRes, 
  336.                                                        timeProc, (DWORD)hWnd,
  337.                                                        TIME_PERIODIC);
  338.                            }
  339.                           
  340.                            if (rc != TIMERR_NOERROR || nTimerRes == 0)
  341.                            {                                            
  342.                                MessageBox(hWnd, "Error setting up timer...", NULL, MB_OK);
  343.                                break;
  344.                            }
  345.  
  346.                            // open MIDI output device for playback
  347.                            //.....................................
  348.  
  349.                            rc = midiOutOpen(&hmo, nDevId, (DWORD)NULL, 
  350.                                            (DWORD)NULL, CALLBACK_NULL);
  351.                            
  352.                            if (rc == MMSYSERR_NOERROR)
  353.                            {
  354.                                eStatus = StatusOn;
  355.                                SetWindowText(hWnd, "MIDI Output - On");
  356.                            }
  357.                            else
  358.                            {
  359.                                midiOutGetErrorText(rc, msg, MSG_LEN);
  360.                                MessageBox(hWnd, msg, NULL, MB_OK);
  361.  
  362.                                // kill timers
  363.                                //............
  364.  
  365.                                timeKillEvent(nTimerId);
  366.                                timeEndPeriod(nTimerRes);
  367.                                break;
  368.                            }
  369.                         }
  370.                         break;
  371.                  
  372.                  case IDM_OFF :
  373.                         {
  374.                            if (eStatus != StatusOn)
  375.                                break; // already off
  376.  
  377.                            // kill timers
  378.                            //............
  379.  
  380.                            timeKillEvent(nTimerId);
  381.                            timeEndPeriod(nTimerRes);
  382.  
  383.                            // shut down MIDI output
  384.                            //......................
  385.  
  386.                            midiOutReset(hmo);
  387.                            midiOutClose(hmo);
  388.                            
  389.                            eStatus = StatusOff;
  390.                            SetWindowText(hWnd, "MIDI Output - Off");
  391.                         }
  392.                         break;
  393.  
  394.                  case IDM_SENDSYSEXCL:
  395.                         {
  396.                            if (eStatus == StatusOn || eStatus == StatusSendSysExcl)
  397.                                break; // already busy
  398.  
  399.                            SendMessage(hListBox, LB_RESETCONTENT, 0, 0);
  400.  
  401.                            // load system exclusive message into data buffer block
  402.                            //.....................................................
  403.  
  404.                            memcpy(pmh->lpData, ExclData, EXCL_LEN);
  405.                            pmh->dwBufferLength = EXCL_LEN;
  406.  
  407.                            // open MIDI output device for playback
  408.                            //.....................................
  409.  
  410.                            rc = midiOutOpen(&hmo, nDevId, (DWORD)hWnd, 
  411.                                             (DWORD)NULL, CALLBACK_WINDOW);
  412.  
  413.                            // prepare data buffer block and send to MIDI device
  414.                            //..................................................
  415.  
  416.                            rc = midiOutPrepareHeader(hmo, pmh, sizeof(MIDIHDR));
  417.  
  418.                            if (rc == MMSYSERR_NOERROR)
  419.                                rc = midiOutLongMsg(hmo, pmh, sizeof(MIDIHDR));
  420.  
  421.                            if (rc == MMSYSERR_NOERROR)
  422.                            {
  423.                                eStatus = StatusSendSysExcl;
  424.                                SetWindowText(hWnd, "Sending MIDI system exclusive message");
  425.                            }
  426.                            else
  427.                            {
  428.                                midiOutGetErrorText(rc, msg, MSG_LEN);
  429.                                MessageBox(hWnd, msg, NULL, MB_OK);
  430.                                break;
  431.                            }
  432.                         }
  433.                         break;
  434.  
  435.                  case IDM_ABOUT :
  436.                         DialogBox( hInst, "AboutBox", hWnd, About );
  437.                         break;
  438.  
  439.                  case IDM_EXIT :
  440.                         DestroyWindow( hWnd );
  441.                         break;
  442.               }
  443.               break;
  444.  
  445.       case USR_SHORTMSG:
  446.               {
  447.                  // display short message sent to the MIDI output device
  448.                  //.....................................................
  449.  
  450.                  sprintf(msg, "Short Message: %x %x %x",
  451.                          LOBYTE( LOWORD(lParam) ), 
  452.                          HIBYTE( LOWORD(lParam) ),   
  453.                          LOBYTE( HIWORD(lParam) ) );
  454.                  SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)msg);
  455.               }
  456.               break;
  457.       
  458.       case MM_MOM_DONE:
  459.               {
  460.                  DWORD     i;
  461.                  char      cTmp[33];
  462.                  LPMIDIHDR lpMidiHdr = (LPMIDIHDR)lParam;
  463.  
  464.                  // check to see if the buffer is ready to be processed
  465.                  //....................................................
  466.  
  467.                  if ( !(lpMidiHdr->dwFlags & MHDR_DONE) )
  468.                      break;
  469.  
  470.                  // display the system exclusive MIDI message
  471.                  //..........................................
  472.  
  473.                  if (lpMidiHdr->dwBufferLength > 0)
  474.                  {
  475.                      lstrcpy(msg, "System Exclusive: ");
  476.  
  477.                      for (i = 0; i < lpMidiHdr->dwBufferLength && 
  478.                                  lstrlen(msg) < MSG_LEN - 4; i++)
  479.                      {
  480.                         sprintf(cTmp, " %02x", lpMidiHdr->lpData[i]);
  481.                         lstrcat(msg, cTmp);
  482.                      }
  483.                      
  484.                      SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)msg);
  485.                  }
  486.  
  487.                  // unprepeare the data buffer block
  488.                  // and close the MIDI output device
  489.                  //.................................
  490.  
  491.                  midiOutPrepareHeader(hmo, lpMidiHdr, sizeof(MIDIHDR));
  492.                  midiOutReset(hmo);
  493.                  midiOutClose(hmo);
  494.  
  495.                  eStatus = StatusOff;
  496.                  SetWindowText(hWnd, "MIDI Output - Off");
  497.               }
  498.               break;
  499.  
  500.       case WM_DESTROY :
  501.  
  502.               // kill timers
  503.               //............
  504.  
  505.               timeKillEvent(nTimerId);
  506.               timeEndPeriod(nTimerRes);
  507.  
  508.               // shut down MIDI output
  509.               //......................
  510.  
  511.               if (hmo)
  512.               {
  513.                   midiOutReset(hmo);
  514.  
  515.                   //midiInUnprepareHeader(hmi, pmh, sizeof(MIDIHDR));
  516.  
  517.                   midiOutClose(hmo);
  518.               }
  519.        
  520.               // free the MIDIHDR buffer block
  521.                //..............................
  522.  
  523.               if (pmh != NULL)
  524.               {
  525.                   HeapFree(GetProcessHeap(), 0, pmh->lpData);                 
  526.                   HeapFree(GetProcessHeap(), 0, pmh);
  527.                   pmh = NULL;
  528.                }
  529.  
  530.               PostQuitMessage(0);
  531.               break;
  532.  
  533.       default :
  534.             return( DefWindowProc( hWnd, uMsg, wParam, lParam ) );
  535.    }
  536.  
  537.    return( 0L );               
  538. }
  539.  
  540. VOID CALLBACK timeProc(UINT nTimerId, UINT msg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
  541. {
  542.    DWORD i;
  543.  
  544.    // send all MIDI short messages that match
  545.    // the current ms time
  546.    //........................................
  547.  
  548.    for (i = MidiSong.dwNextMsg; 
  549.         i < SONG_LEN && MidiSong.ms == MidiMsg[i].ms; 
  550.         i++)
  551.    {
  552.       midiOutShortMsg(hmo, MidiMsg[i].dwMidiMsg);
  553.       MidiSong.dwNextMsg = i + 1;
  554.  
  555.       PostMessage((HWND)dwUser, USR_SHORTMSG, 0, MidiMsg[i].dwMidiMsg);
  556.    }
  557.  
  558.    MidiSong.ms++;  // incr. the current time
  559.    
  560.    // check if we have hit the end of the of the MIDI song
  561.    // if so, shut down the timer
  562.    //.....................................................
  563.  
  564.    if (MidiSong.dwNextMsg >= SONG_LEN)
  565.    {
  566.        timeKillEvent(nTimerId);
  567.        PostMessage((HWND)dwUser, WM_COMMAND, IDM_OFF, 0);
  568.    }
  569. }
  570.  
  571.  
  572. LRESULT CALLBACK About( HWND hDlg,           
  573.                         UINT message,        
  574.                         WPARAM wParam,       
  575.                         LPARAM lParam)
  576. {
  577.    switch (message) 
  578.    {
  579.        case WM_INITDIALOG: 
  580.                return (TRUE);
  581.  
  582.        case WM_COMMAND:                              
  583.                if (   LOWORD(wParam) == IDOK         
  584.                    || LOWORD(wParam) == IDCANCEL)    
  585.                {
  586.                        EndDialog(hDlg, TRUE);        
  587.                        return (TRUE);
  588.                }
  589.                break;
  590.    }
  591.  
  592.    return (FALSE); 
  593. }
  594.  
  595.  
  596.  
  597.  
  598.  
  599.  
  600.  
  601.  
  602.  
  603.  
  604.